﻿using System;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.Win32;
using MouseExtender.Logic.Managers;

namespace MouseExtender.Logic.Common
{
    public class Logger
    {
        public static event EventHandler CriticalErrorOccurred;
        private static readonly string _netFrameworkVersionsInstalled = null;
        private static readonly string _logFileName = null;
        private static object _syncRoot = new object();

        static Logger()
        {
            string userAgentWithFrameworkVersions = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\5.0\User Agent\Post Platform";
            RegistryKey componentsKey = Registry.LocalMachine.OpenSubKey(userAgentWithFrameworkVersions);
            _netFrameworkVersionsInstalled = String.Join(";", componentsKey.GetValueNames());

            Assembly meAssembly = Assembly.GetExecutingAssembly();
            string currentDirectory = Path.GetDirectoryName(meAssembly.Location);
            _logFileName = Path.Combine(currentDirectory, Constants.LogFileName);
        }

        public Guid LastEventId
        {
            get;
            private set;
        }

        public Exception CriticalErrorException
        {
            get;
            private set;
        }

        private Logger()
        {
        }

        private static Logger HandleError(Exception ex, bool isCritical, bool sendErrorReport)
        {
            Logger logger = new Logger()
            {
                LastEventId = Guid.NewGuid(),
                CriticalErrorException = ex
            };
            logger.LogCriticalError();

            if (sendErrorReport)
            {
                //submit error report
                ErrorReportManager.SubmitReport(logger.LastEventId, isCritical, ex.Message, ex.ToString());
            }

            return logger;
        }

        public static void HandleCriticalError(Exception ex)
        {
            bool sendReport = true;
            //filter com exception
            if (ex.Message.Contains(Constants.GraphicsCOMExceptionId))
            {
                sendReport = false;
            }

            Logger logger = HandleError(ex, true, sendReport);

            if (CriticalErrorOccurred != null)
                CriticalErrorOccurred.Invoke(logger, new EventArgs());
        }

        public static void HandleNonCriticalError(Exception ex)
        {
            HandleError(ex, false, true);
        }

        public static void HandleNonCriticalError(Exception ex, bool sendErrorReport)
        {
            HandleError(ex, false, sendErrorReport);
        }

        public static void Message(string message)
        {
            lock (_syncRoot)
            {
                using (StreamWriter streamWriter = new StreamWriter(_logFileName, true))
                {
                    string logMessage = String.Format("{1}{0}", Environment.NewLine, message);
                    streamWriter.Write(logMessage);
                }
            }
        }

        private void LogCriticalError()
        {
            string exceptionNativeCode = null;
            Type exceptionType = CriticalErrorException.GetType();
            if (exceptionType == typeof(System.ComponentModel.Win32Exception))
            {
                exceptionNativeCode = (CriticalErrorException as Win32Exception).NativeErrorCode.ToString();
            }
            string exceptionData = GetExceptionDataAsString();
            lock (_syncRoot)
            {
                using (StreamWriter streamWriter = new StreamWriter(_logFileName, true))
                {
                    string logMessage = String.Format("ErrorID: {0}\r\nDate: {1}\r\nMessage: {2}\r\nType: {3}\r\nOS: {4}\r\nFramework version: {5}\r\nData: {6}\r\nNative Code: {7}\r\nStackTrace: {8}\r\n\r\n",
                            LastEventId,
                            DateTime.Now,
                            CriticalErrorException.Message,
                            exceptionType.FullName,
                            Environment.OSVersion.VersionString,
                            _netFrameworkVersionsInstalled,
                            exceptionData,
                            exceptionNativeCode,
                            CriticalErrorException.StackTrace
                            );
                    streamWriter.Write(logMessage);
                    if (CriticalErrorException.InnerException != null)
                    {
                        logMessage = String.Format("Inner exception: {0}\r\n", CriticalErrorException.InnerException);
                        streamWriter.Write(logMessage);
                    }
                }
            }
        }

        private string GetExceptionDataAsString()
        {
            if (CriticalErrorException.Data != null)
            {
                StringBuilder sbData = new StringBuilder();
                foreach (object key in CriticalErrorException.Data.Keys)
                {
                    object value = CriticalErrorException.Data[key];
                    sbData.AppendFormat("{0}: {1}|", key, value);
                }
                return sbData.ToString();
            }
            return null;
        }
    }
}
